


#include "Camera.h"

#include "Simulation.h"
#include "SDLview.h"


Camera* Camera::_instance = NULL;

Camera* Camera::Instance()
{
	if (_instance == NULL)
	{
		_instance = new Camera;
	}
	return _instance;
}

Camera::Camera() 
{	
	m_near = 0.01; 
	m_far = 1000.0;
	m_fovy = 60.0; 
	m_avoidCosine = cos(M_PI/180.f);
	
	m_viewTarget.setZero3();
	m_viewPoint = Vec4(1,1,1);
	m_azimuth = -0.25f*M_PI;
	m_phi = 3.f*M_PI/4.f;
	
	m_localX = Vec4(1,0,0);
	m_localY = Vec4(0,1,0);
	
	m_stepLength = 0.01f;
}

void Camera::InitObserver()
{
	//observer properties
	int dims[2];
	SDLview::Instance()->getViewDims(dims);
	m_aspect = (float)dims[0] / (float)dims[1];	
}


void Camera::SetObserverProjection(void)
{			
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(m_fovy, m_aspect, m_near, m_far);		
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	float arena_edge_size = 100.f;
	Vec4 ray = m_viewPoint + GetViewDirection() * arena_edge_size;
	
	Vec4 y = GetLocalY();
	
	gluLookAt( m_viewPoint[0], m_viewPoint[1], m_viewPoint[2],
			   ray[0],         ray[1],         ray[2],
			   y[0],           y[1],           y[2]    );		
}

Vec4 Camera::GetViewDirection()
{
	float theta = 0.5f*M_PI - m_azimuth;
	float x = cos(m_phi) * sin(theta);
	float y = sin(m_phi) * sin(theta);
	float z = cos(theta);
	
	Vec4 dir(x, y, z);
	dir.normalizeIfNotZero();
	
	return dir;
}

const Vec4& Camera::GetViewPoint()
{
	return m_viewPoint;
}

Vec4 Camera::GetLocalY()
{
	float theta = 0.5f*M_PI - m_azimuth;
	float x = -cos(m_phi) * cos(theta);
	float y = -sin(m_phi) * cos(theta);
	float z = sin(theta);
	return Vec4(x, y, z);
}

Vec4 Camera::GetLocalX()
{
	float x = sin(m_phi);
	float y = -cos(m_phi);
	return Vec4(x, y, 0.0f);
}

void Camera::setView(const Vec4& v)
{
	float x = v[0];
	float y = v[1];
	float z = v[2];
	
	m_azimuth = 0.5f*M_PI - acos(z);
	m_phi = atan2(y, x);
}

void Camera::RotateViewAboutVertical(float dAngle)
{
	m_phi -= dAngle;
	m_phi = fmod(m_phi, 2.0f*float(M_PI));
}

void Camera::RotateViewAboutHorizontal(float dAzimuth)
{
	float newAzimuth = m_azimuth + dAzimuth;
	float cosTheta = cos(0.5f*M_PI - newAzimuth);
	
	if ( cosTheta>m_avoidCosine || cosTheta<-m_avoidCosine )
	{
		return;
	}
	
	m_azimuth = newAzimuth; 
}

void Camera::goForward()
{
	m_viewPoint += GetViewDirection() * 2.0f * m_stepLength;
}

void Camera::goBackward()
{
	m_viewPoint -= GetViewDirection() * 2.0f * m_stepLength;
}

void Camera::goLeft()
{
	m_viewPoint -= GetLocalX() * 2.0f * m_stepLength;
}

void Camera::goRight()
{
	m_viewPoint += GetLocalX() * 2.0f * m_stepLength;
}


